﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using Co9320.NamaAlert.Shared;
using Co9320.NamaAlert.Server.Parameters;

namespace Co9320.NamaAlert.Model
{
	/// <summary>
	/// メソッドコール同期型の､C/Sコミュニケータクラスです｡
	/// </summary>
	/// <remarks>
	/// 仕様上の便宜性の理由から､ApplicationConfigに依存しない､
	/// ハードコードされたバインドと､コンストラクタで指定されたエンドポイントアドレスを用いて
	/// 通信環境の構築を行います｡
	/// これは､ApplicationConfigが存在しない環境において､このクラスを利用した場合､接続の確率が
	/// 成功する反面､サーバー側のバインド設定が変更された際は
	/// ､ソースコード内のバインド設定を変更しなければならないことを意味します｡
	/// また､コネクションは常に確立した状態になります｡
	/// </remarks>
	public class ImmidiateCommunicator:IDisposable
	{

		private TokenProcessor Processor;
		private Co9320.NamaAlert.Server.ITagServer client;
		private ChannelFactory<Co9320.NamaAlert.Server.ITagServer> factory;

		/// <summary>
		/// Ctor
		/// </summary>
		/// <param name="ApplicationId">ApplicationIdを指定します｡</param>
		/// <param name="KeySeed">KeySeedを指定します｡</param>
		/// <param name="EndPointAddress">接続先エンドポイントのアドレスを指定します｡</param>
		public ImmidiateCommunicator(string ApplicationId, string KeySeed,string EndPointAddress)
		{
			if (ApplicationId == null)
			{
				throw new ArgumentNullException("ApplicationId");
			}

			if (KeySeed == null)
			{
				throw new ArgumentNullException("KeySeed");
			}

			if (EndPointAddress == null)
			{
				throw new ArgumentNullException("EndPoint");
			}

			BasicHttpBinding bind = new BasicHttpBinding(BasicHttpSecurityMode.None);
			bind.TransferMode = TransferMode.Buffered;
			bind.TextEncoding = System.Text.Encoding.UTF8;
			bind.UseDefaultWebProxy = true;
			bind.MessageEncoding = WSMessageEncoding.Text;
			bind.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;

			factory = new ChannelFactory<Co9320.NamaAlert.Server.ITagServer>(bind, EndPointAddress);

			factory.Open();
			client = factory.CreateChannel();

			Processor = new TokenProcessor(ApplicationId, KeySeed);
		}

		/// <summary>
		/// タグを追加します｡
		/// 既に､追加済みのタグを追加した際は､何も行いません｡
		/// </summary>
		/// <param name="StationId">StationIdを指定します｡</param>
		/// <param name="Caster">Casterを指定します｡</param>
		/// <param name="Tags">Tagを指定します｡Userタグのみ処理されます｡</param>
		public void Add(string StationId, string Caster, IList<TagModel> Tags)
		{
			List<AddDeleteParamerter> Params = new List<AddDeleteParamerter>();
			Params.Add(CreateParameter(StationId, Caster, Tags));

			client.Add(CreateToken(), Params);
		}

		/// <summary>
		/// タグを削除します｡
		/// </summary>
		/// <param name="StationId">StationIdを指定します｡</param>
		/// <param name="Caster">Casterを指定します｡</param>
		/// <param name="Tag">Tagを指定します｡Userタグのみ処理されます｡</param>
		public void Delete(string StationId, string Caster, TagModel Tag)
		{
			List<AddDeleteParamerter> Params = new List<AddDeleteParamerter>();
			Params.Add(CreateParameter(StationId, Caster, Tag));

			client.Delete(CreateToken(), Params);
		}

		/// <summary>
		/// タグを取得します｡
		/// </summary>
		/// <param name="StationId">StationIdを指定します｡</param>
		/// <param name="Caster">Casterを指定します｡</param>
		/// <returns>取得されたタグが戻ります｡存在しない場合は､Count=0のGenericListが戻ります｡</returns>
		public IList<TagModel> Get(string StationId, string Caster)
		{
			List<GetParameter> Params = new List<GetParameter>();
			Params.Add(CreateParameter(StationId, Caster));

			GetReturn ret = client.GetTags(CreateToken(), Params);

			List<TagModel> list = new List<TagModel>();

			foreach (var elem in ret.TagInformations)
			{
				foreach (var tag in elem.TagValues)
				{
					list.Add(new TagModel(tag, TagKind.User));
				}
			}

			return list;

		}

		private AuthorizationTokenParameter CreateToken()
		{
			AuthorizationTokenParameter ret = new AuthorizationTokenParameter();
			ret.AppricationId = Processor.ApplicationId;
			ret.Time = TokenProcessor.GetTime();
			ret.HashValue = Processor.GetToken(ret.Time);

			return ret;
		}

		private static GetParameter CreateParameter(string StationId, string Caster)
		{
			GetParameter param = new GetParameter();
			param.Caster = Caster;
			param.StationId = StationId;

			return param;
		}

		private static AddDeleteParamerter CreateParameter(string StationId, string Caster, TagModel Tag)
		{
			return CreateParameter(StationId, Caster, new List<TagModel> { Tag });
		}

		private static AddDeleteParamerter CreateParameter(string StationId, string Caster,IList<TagModel> Tags)
		{
			AddDeleteParamerter Param = new AddDeleteParamerter();
			Param.Caster = Caster;
			Param.StationId = StationId;
			Param.TagValue = new List<string>();

			foreach (var elem in Tags)
			{
				if (elem.Kind == TagKind.User)
				{
					Param.TagValue.Add(elem.Value);
				}
				else
				{
					continue;
				}
			}

			return Param;

		}

		#region IDisposable メンバ

		/// <summary>
		/// このクラスを破棄します｡
		/// </summary>
		public void Dispose()
		{
			factory.Abort();
		}

		#endregion
	}
}
